package gas.frontend;

import 	java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * @authors kuangye, david
 *
 */
public class IntermediateCodeGenerator {
	// This is for the output of the IR file.
	private String irFilename;
	private StorageMap symtbl;
	private HashMap<String, Integer> labelList = new HashMap<String, Integer>();
	private int labelCount = 0;
	
	public IntermediateCodeGenerator(String irFilename, StorageMap symtbl){
		this.irFilename = irFilename;
		this.symtbl = symtbl;
	}
	
    public String StringTranform(String x){
		String returnValue;
		if(x.startsWith("\"") && x.endsWith("\"")){
			returnValue = "$" + x.substring(1, x.length() - 1);	
		}
		else returnValue = x;
    	return returnValue;
    }
		
	/**
	 * @return label
	 * generate new label for goto
	 */
	public String NewLabel(){
		labelCount++;
		String label = new String("L" + String.valueOf(labelCount));
		labelList.put(label, new Integer(labelCount));
		return label;
	}
	
    /**
     * @param assign_stmt lValue expression
     * for assign_stmt : lvalue ASGN expression SEMI
     */
    public void ProcessAssignment(TreeNode assign_stmt, TreeNode lValue, TreeNode expression){
    	//TODO: type-check and type conversion
    	assign_stmt.code = expression.code + (lValue.addr + " " + expression.addr + "\n");
    }
    
    public void ProcessWhile(TreeNode while_stmt, TreeNode expression, TreeNode stmt, String Lini, String Lter){
    	String L1 = Lini;
    	String L2 = Lter;
    	while_stmt.code = "Label " + L1 + "\n" + expression.code;
    	while_stmt.code += "iffalse " + expression.addr + " goto " + L2 + "\n";
    	while_stmt.code += stmt.code + "goto " + L1 + "\n" + "Label " +L2 + "\n";
    	
    }
    
    
    /**
     * @param for_stmt ini_stmt test_stmt inc_stmt body_stmt
     * for_stmt : FOR LPAREN expression SEMI expression SEMI expression RPAREN stmt
     */
    public void ProcessFor(TreeNode for_stmt, TreeNode ini_stmt, TreeNode test_stmt, TreeNode inc_stmt, TreeNode body_stmt, String Lini, String Lter){
    	for_stmt.code = ini_stmt.code;
    	String gotoTmp1 = NewLabel();
    	for_stmt.code += "Label " + gotoTmp1 + "\n" + test_stmt.code;
    	String gotoTmp2 = Lter;
    	String gotoTmp3 = Lini;
    	for_stmt.code += "iffalse " + test_stmt.addr + " goto " + gotoTmp2 + "\n";
    	for_stmt.code += body_stmt.code;
    	for_stmt.code += "Label " + gotoTmp3 + "\n";
    	for_stmt.code += inc_stmt.code;
    	for_stmt.code += "goto " + gotoTmp1 + "\n";
    	for_stmt.code += "Label " + gotoTmp2 + "\n";
    }

    /**
     * @param decl_stmt id
     * for TYPE ID SEMI  
     * 	   TYPE ID ASGN NEW TYPE LPAREN RPAREN SEMI
     */
    //     TODO: may need to be changed to handle parameters, e.g., new foo(5, 6)
    public void ProcessDeclaration(TreeNode decl_stmt, String id){
    	decl_stmt.addr = id; 
    	decl_stmt.code = decl_stmt.addr + " " + decl_stmt.type + "\n";
    }
    
    
    /**
     * @param decl_stmt expression id
     * for TYPE ID ASGN expression SEMI
     */
    public void ProcessDeclaration(TreeNode decl_stmt, TreeNode expression, String id){
    	decl_stmt.code = expression.code;    	
    	decl_stmt.addr = id; 
    	decl_stmt.code += decl_stmt.addr + " " + decl_stmt.type + " " + expression.addr + "\n";
    	}
    

    /**
     * @param decl_stmt size expression id
     * for TYPE ID LBLK NUM RBLK ASGN LBRACE expression opt_array_init_list RBRACE SEMI
     */
    
    public void ProcessDeclaration(TreeNode decl_stmt, int size, TreeNode expression, TreeNode array_list, String id, ArrayList<String> list) {
    	decl_stmt.code = expression.code;
    	decl_stmt.addr = id; 
        decl_stmt.code += array_list.code;
    	decl_stmt.code += decl_stmt.addr + " " + decl_stmt.type + "#" + String.valueOf(size) + "\n";
        decl_stmt.code += decl_stmt.addr + "#0 " + expression.addr + "\n";
        for (int i = 1; i < size; i++) {
        	decl_stmt.code += decl_stmt.addr + "#" + String.valueOf(i)+ " " + list.get(size-i-1) + "\n";
        }
    }
    
    /**
     * @param decl_stmt size id
     * for TYPE ID LBLK RBLK ASGN NEW TYPE LBLK NUM RBLK SEMI
     */
    public void ProcessDeclaration(TreeNode decl_stmt, int size, String id){
    	decl_stmt.addr = id;
    	decl_stmt.code = decl_stmt.addr + " " + decl_stmt.type + "#" + String.valueOf(size) + "\n";    	
    }
    

        
    //  ID LPAREN act_arg_list RPAREN SEMI

    public void ProcessFunCall(TreeNode func_call_stmt, TreeNode act_arg_list, String funcName, ArrayList<String>list) {
    	func_call_stmt.code = act_arg_list.code;
    	func_call_stmt.code += "funcall " + func_call_stmt.addr + " fun_" + funcName;
    	for (int i = 0;i<list.size();i++)
    	{
    		func_call_stmt.code += " " + list.get(list.size()-i-1);
    	}
    	func_call_stmt.code += '\n';

    }
    
 public void ProcessConstructor(TreeNode decl_stmt, int size, TreeNode actarglist, String newID, ArrayList<String> list) {
        decl_stmt.code = actarglist.code;
        decl_stmt.code += "funcall " + newID + " fun_" + decl_stmt.type;
        for (int i = 0; i < size; i++) {
            decl_stmt.code += " " + list.get(size-i-1);    
        }
        decl_stmt.code += '\n';
    }

 public void ProcessFunCall(TreeNode lvalue, TreeNode func_call_stmt, TreeNode act_arg_list, String funcName, ArrayList<String>list) {
 	func_call_stmt.code = act_arg_list.code;
 	func_call_stmt.code += "funcall" + " " + func_call_stmt.addr + " fun_" + funcName + "@" + lvalue.addr;
 	for (int i = 0;i<list.size();i++)
 	{
 		func_call_stmt.code += " " + list.get(list.size()-i-1);
 	}
 	func_call_stmt.code += '\n';

 }
 
    
    // When the traversal goes to the root TreeNode, output it into the IR file.
    public void PrintToFile(TreeNode root){
    	try {
			FileWriter outFile = new FileWriter(irFilename);
			PrintWriter out = new PrintWriter(outFile);
			
			out.print(root.code);
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
    }
    

}